package data;

import java.util.ArrayList;
import java.util.Hashtable;

import competences.ListeCompetences;
import competences.ECompetence;

import classes.Classe;
import dons.ListeDons;
import equipement.EArme;
import equipement.EArmure;
import equipement.EMateriel;
import equipement.Equipement;

public class Personnage {
	
	private String nom;
	private int vie;
	private int niveauGlobal;
	private int caracteristiques[];
	private int absorption;
	private int initiative;
	private int classeToucher;
	private int classeEsquive;
	private int classeParade;
	private ListeCompetences competences;
	private ListeDons dons;
	private ArrayList<Attaque> attaques;
	private Equipement equipement;
	private ArrayList<String> traitsPhysiques;
	private ArrayList<String> traitsMoraux;
	
	private Classe classe;
	
	public Personnage (String nom, Classe classe) {
		initialisation (nom, classe);
	}
	
	public Personnage (String nom, Classe classe, int niveau) {
		initialisation (nom, classe);
		
		while (niveauGlobal != niveau)
			niveauSup(classe);
	}
	
	private void initialisation (String nom, Classe classe) {
		this.caracteristiques = new int[ECaracteristique.values().length];
		this.attaques = new ArrayList<Attaque> ();
		this.traitsPhysiques = new ArrayList<String> ();
		this.traitsMoraux = new ArrayList<String> ();
		this.dons = new ListeDons (caracteristiques);
		this.competences = new ListeCompetences (caracteristiques);
		
		this.nom = nom;
		this.classe = classe;
		this.niveauGlobal = this.classe.getNiveau();
		
		
		generateCaracteristiques ();
		calculVie ();
		calculInitiative ();
		majClasseTEP ();
		
		for (int i=0; i<4; i++)
			generateCompetences ();
		
		generateTraitsMoraux ();
		generateTraitsPhysiques ();
		
		generateEquipement();
		majAttaque ();

		calculAbsorption ();
		
		this.classe.AdjustSort(caracteristiques);
		
	}
	
	private void generateCaracteristiques () {
		int nbCaracs = ECaracteristique.values().length;
		Hashtable<Integer,ECaracteristique> ordreCaracs = new Hashtable<Integer,ECaracteristique> ();
		int nbCaracsRestantes = nbCaracs;
		int[] tabCaracsTirees = new int[nbCaracs];
		
		for (int i = 0; i < nbCaracs; i++) {
			tabCaracsTirees[i] = 0;
			caracteristiques[i] = 0;
		}
		
		//Tirage d'un ordre aleatoire des caracteristiques
		for (ECaracteristique c : ECaracteristique.values()) {
			int position = De.lancer(nbCaracsRestantes)-1;
			while (ordreCaracs.containsKey(position)) {
				position++;
			}
			ordreCaracs.put(position, c);
			
			nbCaracsRestantes--;
		}
		
		//Tirages des caracteristiques et mise en ordre (plus petit au plus grand)
		for (int i = 0; i < nbCaracs; i++) {
			int lance = De.lancerCarac();
			int position = 0;
			boolean mis = false;
			while (!mis) {
				if (tabCaracsTirees[position] == 0){
					tabCaracsTirees[position] = lance;
					mis = true;
				}
				else if (tabCaracsTirees[position] > lance) {
					for (int j = nbCaracs-1; j > position; j--)
						tabCaracsTirees[j]=tabCaracsTirees[j-1];
					tabCaracsTirees[position] = lance;
					mis = true;
				}
				else {
					position++;
				}
			}
		}
		
		//association des deux
		for (int i = 0; i < nbCaracs; i++) {
			boolean mis = false;
			//on met d'abord les tirages les plus faibles aux caracteristiques non favorables
			for (int key = 0; key < nbCaracs; key++) {
				ECaracteristique c = (ECaracteristique) ordreCaracs.get(key);
				if ((!mis) && (caracteristiques[c.ordinal()] == 0) && (!classe.isCaracFavorable(c))) {
					caracteristiques[c.ordinal()] = tabCaracsTirees[i];
					mis = true;
				}
			}
			//lorsqu'il ne reste que des caracteristiques favorables, on met les tirages restant (les forts) dedans
			if (!mis) {
				for (int key = 0; key < nbCaracs; key++) {
					ECaracteristique c = (ECaracteristique) ordreCaracs.get(key);
					if ((!mis) && (caracteristiques[c.ordinal()] == 0)) {
						caracteristiques[c.ordinal()] = tabCaracsTirees[i];
						mis = true;
					}
				}
			}
		}
	}
	
	private void calculVie () {
		int valConstitution = caracteristiques[ECaracteristique.CON.ordinal()];
		if (valConstitution >= 10)
			vie = 10 + (1+(valConstitution-10)/2)*De.lancer(8);
		else if (valConstitution >= 8) {
			vie = valConstitution + De.lancer(6);
		}
		else if (valConstitution >= 6) {
			vie = valConstitution + De.lancer(4);
		}
		else if (valConstitution >= 4) {
			vie = valConstitution + De.lancer(2);
		}
		else {
			vie = valConstitution;
		}
		
	}
	
	private void generateCompetences () {
		ArrayList<ECompetence> competencesDeClasse = classe.getCompetenceDeClasse ();
		int nbCompetence = calculModTrait (ECaracteristique.INT) + classe.getBonusCompetence();
		for (int i=0; i<nbCompetence; i++) {
			boolean competenceMise = false;
			do {
				ECompetence competenceAMettre = competencesDeClasse.get(De.lancer(competencesDeClasse.size())-1);
				competenceMise = competences.addRang(competenceAMettre, niveauGlobal);
			} while (!competenceMise);
		}
	}
	
	private void calculInitiative () {
		initiative = calculModTrait (ECaracteristique.DEX);
	}
	
	//TODO rajouter absorption suivant l'armure
	private void calculAbsorption () {
		absorption = 0;
	}
	
	private void generateTraitsMoraux () {
		int val1 = De.lancer(ETraitMoral.values().length)-1;
		int val2 = De.lancer(ETraitMoral.values().length)-1;
		
		while(val1 == val2)
			val2 = De.lancer(ETraitMoral.values().length);
		
		for (ETraitMoral tm : ETraitMoral.values()) {
			if (tm.ordinal() == val1)
				traitsMoraux.add(tm.toString().replace('_', ' '));
			if (tm.ordinal() == val2)
				traitsMoraux.add(tm.toString().replace('_', ' '));
		}
	}
	
	private void generateTraitsPhysiques () {
		int val1 = De.lancer(ETraitPhysique.values().length)-1;
		int val2 = De.lancer(ETraitPhysique.values().length)-1;
		
		while(val1 == val2)
			val2 = De.lancer(ETraitPhysique.values().length);
		
		for (ETraitPhysique tp : ETraitPhysique.values()) {
			if (tp.ordinal() == val1)
				traitsPhysiques.add(tp.toString().replace('_', ' '));
			if (tp.ordinal() == val2)
				traitsPhysiques.add(tp.toString().replace('_', ' '));
		}
	}
	
	private void generateEquipement() {
		
		this.equipement = new Equipement();
		
		this.equipement.SetEquipement(EArme.EpeeLongueSainteDeFeu, EArme.EpeeLongue, 
									EArmure.CuirasseDeResistanceAuFeu, null, this.caracteristiques);
		this.equipement.AddSacADos(EMateriel.Ration);
		this.equipement.AddSacADos(EMateriel.Ration);
		this.equipement.AddSacADos(EArme.ArcCourt);
		this.equipement.getArmure().setPv(60);
	}
	
	//TODO faire un enum de classe pour pouvoir choisir la classe qu'on augmente (multi-classe)
	public void niveauSup () {
		niveauSup (classe);
	}
	
	private void niveauSup (Classe classe) {
		niveauGlobal++;
		classe.levelUp();
		
		if (niveauGlobal%4 == 0) {
			gainCaracteristiques (classe);
			
			//Update les degats des armes
			equipement.getArmeR().SetDegatCarac(caracteristiques);
			if(equipement.getArmeL() != null)
				equipement.getArmeL().SetDegatCarac(caracteristiques);
			
		}
		
		majClasseTEP ();
		majAttaque ();
		generateCompetences ();
		
		classe.AdjustSort(caracteristiques);
	}
	
	private void gainCaracteristiques (Classe classe) {
		ArrayList<Integer> caracsFavorables = new ArrayList<Integer> ();
		for (ECaracteristique c : ECaracteristique.values()) {
			if (classe.isCaracFavorable(c))
				caracsFavorables.add(c.ordinal());
		}
		caracteristiques[caracsFavorables.get(De.lancer(caracsFavorables.size())-1)]++;
	}
	
	//TODO rajouter taille quand les races seront faites
	private void majClasseTEP () {
		int bba = classe.getBba();
		int modDex = calculModTrait (ECaracteristique.DEX);
		int modFor = calculModTrait (ECaracteristique.FOR);
		
		classeToucher = 10 + modDex;
		classeEsquive = bba + modDex;
		classeParade = bba + modFor;
		
	}

	private void majAttaque (){
		int bba = classe.getBba();
		int modFor = calculModTrait (ECaracteristique.FOR);
		int modDex = calculModTrait (ECaracteristique.DEX);
		int bonusTotal = 0;
		Attaque atak;
		
		//Calcul du bonus total (d�pent de l'arme)
		switch(this.equipement.getArmeR().getArme())
		{
		case Poing :
		case EpeeLongue :
		case EpeeADeuxMains :
			bonusTotal = bba + modFor;
			break;
		case EpeeLongueSainteDeFeu :
			bonusTotal = bba + modFor + 3;
			break;
		case ArcLong :
		case ArcCourt :
		case ArbaleteLourde :
			bonusTotal = bba + modDex;
			break; 
		}
		
		//Si il utilise 2 armes
		if(this.equipement.getArmeL() != null) 
			bonusTotal -= 6 ;
		
		//Suppression des attaques pr�c�dentes
		attaques.clear();
		
		//creation des attaques
		do{
			atak = new Attaque(bonusTotal, this.equipement.getArmeR().getArme());
			attaques.add(atak);
			bonusTotal -= 5;
			bba -=5;
		}while(bba>0);
		
		//Calcul du bonus main gauche (si necessaire)
		if(this.equipement.getArmeL() != null) {
			bba = classe.getBba() - 10;
			//Calcul du bonus total (d�pent de l'arme)
			switch(this.equipement.getArmeL().getArme())
			{
			case Poing :
			case EpeeLongue :
			case EpeeADeuxMains :
				bonusTotal = bba + modFor;
				break;
			case EpeeLongueSainteDeFeu :
				bonusTotal = bba + modFor + 3;
				break;
			case ArcLong :
			case ArcCourt :
			case ArbaleteLourde :
				bonusTotal = bba + modDex;
				break; 
			}
			
			//creation de l'attaques
			atak = new Attaque(bonusTotal, this.equipement.getArmeL().getArme());
			attaques.add(atak);
			
		}
	}
	
	private int calculModTrait (ECaracteristique carac) {
		int modTrait = caracteristiques[carac.ordinal()]-10;
		if (modTrait < 0)
			modTrait--;
		modTrait /= 2;
		
		return modTrait;
	}
	
	@Override
 	public String toString() {
		String res = "Personnage :\n" +
				"Nom : " + nom + "\n" +
				"Vie : " + vie + "\n" +
				"Niveau global : " + niveauGlobal + "\n" +
				"Caracteristiques : ";
		
		for (ECaracteristique c : ECaracteristique.values())
			res += c.toString() + " " + caracteristiques[c.ordinal()] + ", ";
		
		res = res.substring(0, res.lastIndexOf(',')) + "\n" +
				"Absorption : " + absorption + "\n" +
				"Initiative=" + initiative + "\n" +
				"Classe de toucher : " + classeToucher + "\n" +
				"Classe d'esquive : " + classeEsquive + "\n" +
				"Classe de parade : " + classeParade + "\n" +
				"Attaques : " + attaques + "\n" +
				"TraitsPhysiques : " + traitsPhysiques + "\n" +
				"TraitsMoraux : " + traitsMoraux + "\n" +
				"\nClasse :\n" + classe +
				"\nEquipement : " + "\n" + equipement + "\n" +
				"\n" + competences + "\n";
		
		return res;
	}
	
	
}
